#include "Model.h"

namespace OCPP
{
	Model::Model()
		:mShadowMapFrameBuffer(NULL)
	{
	}

	Model::~Model()
	{
		CORE_SAFE_DELETE(mShadowMapFrameBuffer)
	}

	void Model::setInstanceMatrix(Matrix4List instanceMatrixs)
	{
		mInstanceMatrixs = instanceMatrixs;
	}

	void Model::loadShader(String vsFileName, String fsFileName, String geomFile)
	{
		mShader.load(vsFileName, fsFileName, geomFile);
	}

	void Model::loadOutlineShader(String vsFileName, String fsFileName)
	{
		mOutlineShader.load(vsFileName, fsFileName);
	}

	void Model::loadShadowShader(String vsFileName, String fsFileName)
	{
		mShadowMapFrameBuffer = new ShadowMapFrameBuffer(2048, 2048);
		mShadowShader.load(vsFileName, fsFileName);
	}

	void Model::load(String fileName)
	{
		setMeshList(MeshBuilder::loadGLTF(fileName, mInstanceMatrixs));
	}

	void Model::drawShadow(Camera& camera, State& shaderState)
	{
		if (mShadowShader.valid())
		{
			mShadowShader.Activate();
			shaderState.updateState(mShadowShader);

			mShadowMapFrameBuffer->bind();
			glClear(GL_DEPTH_BUFFER_BIT);

			// Go over all meshes and draw each one
			for (unsigned int i = 0; i < mMeshList.size(); i++)
			{
				mMeshList[i]->draw(mShadowShader, camera, getModelMatrix());
			}

			mShadowMapFrameBuffer->unbind();
		}
	}

	void Model::draw(Camera& camera, State& shaderState, Shader& shader)
	{
		for (unsigned int i = 0; i < mMeshList.size(); i++)
		{
			mMeshList[i]->draw(shader, camera, getModelMatrix());
		}
	}

	void Model::draw(Camera& camera, State& shaderState)
	{
		mShader.Activate();
		shaderState.updateState(mShader);

		if (mShadowMapFrameBuffer)
		{
			// Bind the Shadow Map
			glActiveTexture(GL_TEXTURE0 + 2);
			glBindTexture(GL_TEXTURE_2D, mShadowMapFrameBuffer->shadowMap);
			mShader.setUniform1i("shadowMap", 2);
		}

		if (mOutlineWidth > 0)
		{
			// Make it so the stencil test always passes
			glStencilFunc(GL_ALWAYS, 1, 0xFF);
			// Enable modifying of the stencil buffer
			glStencilMask(0xFF);
		}

		// Go over all meshes and draw each one
		for (unsigned int i = 0; i < mMeshList.size(); i++)
		{
			mMeshList[i]->draw(mShader, camera, getModelMatrix());
		}

		if (mOutlineWidth > 0)
		{
			if (mOutlineShader.valid())
			{
				// Make it so only the pixels without the value 1 pass the test
				glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
				// Disable modifying of the stencil buffer
				glStencilMask(0x00);
				// Disable the depth buffer
				glDisable(GL_DEPTH_TEST);

				mOutlineShader.Activate();

				shaderState.updateState(mOutlineShader);

				mOutlineShader.setUniformMatrix4fv("camMatrix", camera.getViewProjectMatrix());
				mOutlineShader.setUniform3fv("camPos", camera.getPosition());
				mOutlineShader.setUniform1f("outlining", 0.008f * mOutlineWidth);

				// Go over all meshes and draw each one
				for (unsigned int i = 0; i < mMeshList.size(); i++)
				{
					mMeshList[i]->draw(mOutlineShader, camera, getModelMatrix());
				}

				// Enable modifying of the stencil buffer
				glStencilMask(0xFF);
				// Clear stencil buffer
				glStencilFunc(GL_ALWAYS, 0, 0xFF);
				// Enable the depth buffer
				glEnable(GL_DEPTH_TEST);
			}
		}
	}
}